﻿using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using XfTechOAWeb.EFCore;
using XfTechOAWeb.Service;
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Text;
using System.Reflection;
using XfTechOAWeb.Infrastructure.JWT;
using XfTechOAWeb.Infrastructure.Interceptors;
using XfTechOAWeb.Infrastructure.RedisCache;
using Autofac;
using XfTechOAWeb.Dtos.AutoMapperConfig;
using XfTechOAWeb.ApiServer.Auth;
using Microsoft.AspNetCore.Authorization;
using Newtonsoft.Json;
using XfTechOAWeb.Infrastructure.Filters;
using XfTechOAWeb.ApiServer.Middlewares;
using XfTechOAWeb.EFCore.Base;
using XfTechOAWeb.ApiServer.ServiceExtensions;
using Autofac.Extras.DynamicProxy;
using XfTechOAWeb.Infrastructure.Utilities;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace XfTechOAWeb
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration; // ② 属性 + 构造函数 进行依赖注入
        }

        public IConfiguration Configuration { get; } //此属性用于读取配置

        /// <summary>
        /// 配置服务
        /// </summary>
        /// <param name="services"></param>
        public void ConfigureServices(IServiceCollection services)
        {
            ConfigureAuthentication(services);
            ConfigureAuthorization(services);
            ConfigureControllers(services);

            //注册CSReids服务
            services.AddCSRedisCache(Configuration["RedisConnectionString"]);

            //AutoMapper配置
            services.AddAutoMapper(typeof(ModelToDtoMapperProfile));

            //注册后台任务
            //services.AddHostedService<MyTimerService>(); 

            //swagger的配置
            services.AddSwaggerSetup();

            //用扩展方法封装类了Cors的配置
            services.AddCorsSetup(Configuration["CorsIps"].Split(','));

            //用扩展方法封装类了Db的配置
            services.AddDbSetup();

            //使用 ServiceBasedControllerActivator 替换 DefaultControllerActivator；Controller 默认是由 Mvc 模块管理的，不在 Ioc 容器中。替换之后，将放在 Ioc 容器中。
            services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
        }

        /// <summary>
        /// 配置Autofac IOC容器
        /// </summary>
        /// <param name="builder"></param>
        public void ConfigureContainer(ContainerBuilder builder)
        {
            #region 简单的使用--- 与默认DI容器相同的功能
            //生命周期：默认，也就是瞬时模式==AddTransient
            // services.AddScoped<IStudent, Student>();  --这个是微软自带的写法
            //builder.RegisterType<OrderService>().As<IOrderService>();
            ////生命周期：默认，也就是瞬时模式==AddTransient
            //builder.RegisterType<Student>().As<IStudent>().InstancePerDependency();
            ////生命周期：Scope模式==AddScoped 
            //builder.RegisterType<Student>().As<IStudent>().InstancePerLifetimeScope();
            ////生命周期：单例,AddSingleton
            //builder.RegisterType<Student>().As<IStudent>().SingleInstance();
            #endregion

            builder.RegisterModule<AutofacServicesModuleReg>(); //分模块处理依赖的注入:服务注入配置代码
            builder.RegisterModule<AutofacPropertyModuleReg>(); //分模块处理依赖的注入:属性注入的配置代码
        }

        /// <summary>
        /// 配置管道中间件
        /// </summary>
        /// <param name="app"></param>
        /// <param name="env"></param>
        /// <param name="serviceProvider"></param>
        public void Configure(IApplicationBuilder app, 
                              IWebHostEnvironment env, 
                              IServiceProvider serviceProvider)
        {
            //app.Use((cxt, next) =>
            //{
            //    Console.WriteLine("use请求委托");
            //    return next(); //调用下一个委托
            //});


            ////我们要写一个方法，然后赋值给一个请求委托对象（RequestHandler）
            //app.Run(cxt =>
            //{
            //    Console.WriteLine("我自己写的一个中间件");
            //    return Task.FromResult(1000);
            //});

            //配置HttpContext帮助类
            AppHttpContext.Configure(serviceProvider.GetRequiredService<IHttpContextAccessor>());

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "XfTechOAWeb v1"));
            }

            //静态文件中间件 文件必须在wwwroot根目录下
            app.UseStaticFiles();

            app.UseRouting();

            app.UseCors("跨域策略1");     //配置跨域中间件

            app.UseAuthentication();    //身份认证中间件(验证你是谁？）

            app.UseAuthorization();     //授权中间件（验证你能干什么？）

            //app.UseMiddleware<LogMSGMiddleware>(); //自己日志中间件

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers(); //控制器里面是专注于我们业务系统的业务功能，其他的事情交给 过滤器、中间件
            });

        }

        /// <summary>
        /// 配置认证服务
        /// </summary>
        /// <param name="services"></param>
        private void ConfigureAuthentication(IServiceCollection services)
        {
            //读出appsettings.json中JWTToken配置并与JWTTokenOptions绑定
            JWTTokenOptions JWTTokenOptions = new JWTTokenOptions();
            Configuration.Bind("JWTToken", JWTTokenOptions);

            //配置JwtBearer身份认证服务
            services.AddAuthentication(option =>
            {
                //认证模式
                option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                //质询模式
                option.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(option =>
            {
                option.RequireHttpsMetadata = false;//设置元数据地址或权限是否需要HTTP
                option.SaveToken = true;
                //Token验证参数
                option.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(JWTTokenOptions.Secret)),
                    ValidIssuer = JWTTokenOptions.Issuer,
                    ValidAudience = JWTTokenOptions.Audience,
                    ValidateIssuer = false,
                    ValidateAudience = false,
                    ClockSkew = TimeSpan.Zero,
                    ValidateLifetime = true
                };
                //如果jwt过期，在返回的header中加入Token-Expired字段为true，前端在获取返回header时判断
                option.Events = new JwtBearerEvents()
                {
                    OnAuthenticationFailed = context =>
                    {
                        if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                        {
                            context.Response.Headers.Add("Token-Expired", "true");
                        }
                        return Task.CompletedTask;
                    }
                };
            });

            //读配置方法一
            //JWTTokenOptions JWTTokenOptions = Configuration.GetSection("JWTToken").Get<JWTTokenOptions>();
            //services.AddSingleton<JWTTokenOptions>();

            //读配置方法二，并注册IOptions<JWTTokenOptions>服务
            services.Configure<JWTTokenOptions>(Configuration.GetSection("JWTToken"));

            //注册JWT令牌生成 帮助类
            services.AddSingleton<JwtTokenHelper>();
        }

        /// <summary>
        /// 配置授权服务
        /// </summary>
        /// <param name="services"></param>
        private void ConfigureAuthorization(IServiceCollection services)
        {
            //注册授权服务
            services.AddAuthorization(options =>
            {
                //基于策略的授权
                //options.AddPolicy("Client", policy => policy.RequireRole("Client").Build());
                //options.AddPolicy("Admin", policy => policy.RequireRole("Admin").Build());
                options.AddPolicy("SystemOrAdmin", policy => policy.RequireRole("Admin", "System"));
                //添加自定义策略授权
                //options.AddPolicy("Permission", policy => policy.Requirements.Add(new PermissionAuthorizationRequirement()));
            });

            services.AddSingleton<IAuthorizationHandler, PermissionAuthorizationHandler>(); //依赖注入
        }

        /// <summary>
        /// 注册全局控制器服务，添加全局过滤器
        /// </summary>
        /// <param name="services"></param>
        private void ConfigureControllers(IServiceCollection services)
        {
            services.AddControllers(options =>
            {
                //options.Filters.Add<GlobalLogActionFilter>();       //全局行为过滤器
                //options.Filters.Add<GlobalExceptionFilter>();       //全局异常过滤器
                options.Filters.Add<PermissionAuthorizeFilter>();   //全局自定义权限过滤器
            }).AddNewtonsoftJson(options => { //接口数据 json格式化配置
                // 忽略循环引用
                options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
                // 设置时间格式
                options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";

                // 不使用驼峰
                //options.SerializerSettings.ContractResolver = new DefaultContractResolver();
                // 如字段为null值，该字段不会返回到前端
                // options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
            });
        }
    }
}
